package com.xxxx.comment.service.impl;


import com.xxxx.comment.config.MinIOConfig;
import com.xxxx.comment.config.MinIOConfigProperties;
import com.xxxx.comment.constants.httpcode.HttpConstants;
import com.xxxx.comment.entity.dto.Result;
import com.xxxx.comment.service.FileStorageService;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

@Slf4j
@Service
@EnableConfigurationProperties(MinIOConfigProperties.class)
@Import(MinIOConfig.class)
public class MinIOFileStorageService implements FileStorageService {

    @Resource
    private MinioClient minioClient;

    @Resource
    private MinIOConfigProperties minIOConfigProperties;

    private final static String separator = "/";

    /**
     * MinIO图片上传
     *
     * @param file 上传的图片
     * @return 上传的结果
     */
    @Override
    public Result upload(MultipartFile file)
    {
        // 1.检查参数
        // 1.1 文件是否为空
        if(file == null || file.getSize() == 0)
            return Result.fail(HttpConstants.INVALID_FILE);
        // 1.2 文件大小是否超过15MB
        if (file.getSize() / 1024 / 1024 > 15)
            return Result.fail(HttpConstants.TOO_LARGE_FILE);
        // 1.3 文件名是否为空
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null)
            return Result.fail(HttpConstants.INVALID_FILE_NAME);

        // 2、上传文件
        String uuid = UUID.randomUUID().toString().replace("-", "");
        String ext = originalFilename.substring(originalFilename.lastIndexOf("."));
        String url = null; // 上传到MinIO后返回文件的存储路径
        try {
            // 根据文件拓展名判断文件类型
            if (checkExt(HttpConstants.IMAGE_EXT, ext))
                url = uploadImgFile("", uuid + ext, file.getInputStream());
            else if (checkExt(HttpConstants.HTML_EXT, ext))
                url = uploadHtmlFile("", uuid + ext, file.getInputStream());
            else if (checkExt(HttpConstants.GIT_EXT, ext))
                url = uploadGitFile("", uuid + ext, file.getInputStream());
            else
                return Result.fail(HttpConstants.NOT_SUPPORT_EXT);
            log.warn("文件[{}]上传成功！", url);
        } catch (IOException e) {
            log.error("文件[{}]上传到MinIO失败", originalFilename);
        }

        //4.返回结果
        return Result.ok(url);
    }

    /**
     * 删除文件
     *
     * @param pathUrl 文件全路径
     */
    @Override
    public void delete(String pathUrl) {
        String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");
        int index = key.indexOf(separator);
        String bucket = key.substring(0, index);
        String filePath = key.substring(index + 1);
        // 删除Objects
        RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();
        try {
            minioClient.removeObject(removeObjectArgs);
        } catch (Exception e) {
            log.error("minio remove file error.  pathUrl:{}", pathUrl);
        }
    }


    /**
     * 下载文件
     *
     * @param pathUrl 文件全路径
     * @return 文件流
     */
    @Override
    public byte[] downLoadFile(String pathUrl) {
        String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");
        int index = key.indexOf(separator);
        String bucket = key.substring(0, index);
        String filePath = key.substring(index + 1);
        InputStream inputStream = null;
        try {
            // 获取文件下载输出流
            inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(filePath).build());
        } catch (Exception e) {
            log.error("文件[{}]下载失败", pathUrl);
            return null;
        }

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buff = new byte[100];
        int rc = 0;
        while (true) {
            try {
                Assert.notNull(inputStream, HttpConstants.DOWNLOAD_EMPTY_FILE);
                if (!((rc = inputStream.read(buff, 0, 100)) > 0)) break;
            } catch (IOException e) {
                log.error("文件下载失败[{}]", e.toString());
                return null;
            }
            byteArrayOutputStream.write(buff, 0, rc);
        }
        return byteArrayOutputStream.toByteArray();
    }

    /**
     * 根据日期生成对应存储路径
     *
     * @param prefix   目录前缀
     * @param filename yyyy/mm/dd/file.jpg
     * @return 文件保存路径
     */
    public String buildFilePath(String prefix, String filename) {
        StringBuilder filePath = new StringBuilder(50);
        if (!StringUtils.isEmpty(prefix)) {
            filePath.append(prefix).append(separator);
        }
        // 将今天的日期例如2023-9-25 12:00:00转为目录：2023/09/25
        String todayStr = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
        filePath.append(todayStr).append(separator);
        filePath.append(filename);
        return filePath.toString();
    }

    /**
     * 上传图片文件
     *
     * @param prefix      文件前缀
     * @param filename    文件名
     * @param inputStream 文件流
     * @return 文件全路径
     */
    public String uploadImgFile(String prefix, String filename, InputStream inputStream) {
        // 文件在MinIO中的保存路径
        String filePath = buildFilePath(prefix, filename);
        // 文件在互联网中url地址(目前只能本地访问)
        String url = minIOConfigProperties.getEndpoint() + separator +
                minIOConfigProperties.getBucket() + separator + filePath;
        try {
            // 构建文件参数
            PutObjectArgs putObjectArgs = PutObjectArgs.builder().object(filePath).contentType("image/jpg")
                    .bucket(minIOConfigProperties.getBucket())
                    .stream(inputStream, inputStream.available(), -1).build();
            // 上传文件
            minioClient.putObject(putObjectArgs);
            return url;
        } catch (Exception ex) {
            ex.printStackTrace();
            log.error("上传文件到[{}]失败!", url);
            throw new RuntimeException("文件上传失败!");
        }
    }

    /**
     * 上传html文件
     *
     * @param prefix      文件前缀
     * @param filename    文件名
     * @param inputStream 文件流
     * @return 文件全路径
     */
    public String uploadHtmlFile(String prefix, String filename, InputStream inputStream) {
        String filePath = buildFilePath(prefix, filename);
        String url = minIOConfigProperties.getEndpoint() + separator +
                minIOConfigProperties.getBucket() + separator + filePath;
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object(filePath)
                    .contentType("text/html")
                    .bucket(minIOConfigProperties.getBucket()).stream(inputStream, inputStream.available(), -1)
                    .build();
            minioClient.putObject(putObjectArgs);
            return url;
        } catch (Exception ex) {
            log.error("上传文件[{}]失败", url);
            throw new RuntimeException("文件上传失败!");
        }
    }

    public String uploadGitFile(String prefix, String fileName, InputStream inputStream) {
        String filePath = buildFilePath(prefix, fileName);
        String url = minIOConfigProperties.getEndpoint() + separator +
                minIOConfigProperties.getBucket() + separator + filePath;
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder().object(filePath).contentType("image/gif")
                    .bucket(minIOConfigProperties.getBucket())
                    .stream(inputStream, -1, 5242880).build();
            minioClient.putObject(putObjectArgs);
            return url;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("上传文件[{}]失败", url);
            throw new RuntimeException("文件上传失败!");
        }
    }

    /**
     * 校验文件拓展名
     *
     * @param ext 需要的文件拓展名
     * @param target 待检查的文件拓展名
     * @return 待检查的文件拓展名是否与需要的文件拓展名相同
     */
    public boolean checkExt(String ext, String target) {
        String[] split = null;
        if (ext.contains("/")) {
            split = ext.split("/");
        }
        if (split != null) {
            for (String extension : split) {
                if (("." + extension).equals(target))
                    return true;
            }
            return false;
        }

        return ("." + ext).equals(target);
    }

}
